package com.example.financeapi.utils;


import com.alibaba.fastjson.JSON;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.apache.http.*;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.ContentType;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.util.EntityUtils;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.net.ssl.SSLContext;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;
import javax.servlet.http.HttpServletRequest;
import java.io.*;
import java.net.*;
import java.nio.charset.StandardCharsets;
import java.security.KeyManagementException;
import java.security.NoSuchAlgorithmException;
import java.util.*;

/**
 * http工具类
 * @author Alvin
 */
@Slf4j
public class HttpUtil {

    private static final String UTF_8 = "UTF-8";

    public static void main(String[] args) throws UnknownHostException {
       try {
           InetAddress addr = InetAddress.getLocalHost();
           System.out.println("IP地址：" + addr.getHostAddress() + "，主机名：" + addr.getHostName());
           System.out.println("所有IP："+getLocalIPList().toString());
           System.out.println("所有IP："+getIpAddress().toString());
       } catch (UnknownHostException e) {
           e.printStackTrace();
       }
   }

    /**
     * Creates {@link CloseableHttpClient} instance with default configuration.
     */
    public static CloseableHttpClient createDefault() {
        return HttpClientBuilder.create().build();
    }

    /**
     * GET方法
     * @param uri get地址
     * @return 请求结果
     */
    public static String httpGet(String uri) {
        HttpGet httpGet = null;
        String str = "";
        try (CloseableHttpClient httpClient = HttpUtil.createDefault()) {
            URI url = new URI(uri);
            RequestConfig requestConfig = RequestConfig.custom().setConnectTimeout(2000)
                    .setConnectionRequestTimeout(2000).setSocketTimeout(2000).build();
            httpGet = new HttpGet();
            httpGet.setConfig(requestConfig);
            httpGet.setURI(url);

            HttpResponse response = httpClient.execute(httpGet);
            // 检验返回码
            int statusCode = response.getStatusLine().getStatusCode();
            if (statusCode == HttpStatus.SC_OK) {
                str = EntityUtils.toString(response.getEntity(), UTF_8);
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if (httpGet != null) {
                httpGet.releaseConnection();
            }
        }
        return str;
    }

    /**
     * 以jsonString形式发送HttpPost的Json请求，String形式返回响应结果
     * @param url
     * @param jsonString
     * @return
     * @throws IOException
     */
    public static String sendPostJsonStr(String url, String jsonString) throws IOException {
        if (jsonString == null || jsonString.isEmpty()) {
            return sendPost(url);
        }
        String resp = "";
        StringEntity entityStr = new StringEntity(jsonString, ContentType.create("text/plain", UTF_8));
        CloseableHttpClient httpClient = HttpClients.createDefault();
        HttpPost httpPost = new HttpPost(url);
        httpPost.setEntity(entityStr);
        CloseableHttpResponse response = null;
        try {
            response = httpClient.execute(httpPost);
            HttpEntity entity = response.getEntity();
            resp = EntityUtils.toString(entity, UTF_8);
            EntityUtils.consume(entity);
        } catch (ClientProtocolException e) {
            log.error(e.getMessage());
        } catch (IOException e) {
            log.error(e.getMessage());
        } finally {
            if (response != null) {
                try {
                    response.close();
                } catch (IOException e) {
                    log.error(e.getMessage());
                }
            }
        }
        if (resp == null || resp.equals("")) {
            return "";
        }
        return resp;
    }

    public static String sendPost(String url) throws IOException {
        String result = null;
        // 1.获得一个httpclient对象
        CloseableHttpClient httpclient = HttpClients.createDefault();
        // 2.生成一个post请求
        HttpPost httppost = new HttpPost(url);
        CloseableHttpResponse response = null;
        try {
            // 3.执行get请求并返回结果
            response = httpclient.execute(httppost);
        } catch (IOException e) {
            log.error(e.getMessage());
        }
        // 4.处理结果，这里将结果返回为字符串
        if(response ==null){
            return null;
        }
        HttpEntity entity = response.getEntity();
        try {
            result = EntityUtils.toString(entity);
        } catch (ParseException | IOException e) {
            log.error(e.getMessage());
        }
        return result;
    }

    // post方法
    public static String httpPost(String url, String arg) throws IOException {
        StringBuilder result = new StringBuilder();
        URL targetUrl = new URL(url);
        HttpURLConnection httpConnection = (HttpURLConnection) targetUrl.openConnection();
        httpConnection.setRequestMethod("POST");
        httpConnection.setRequestProperty("Content-Type", "application/json");
        httpConnection.setDoOutput(true);
        httpConnection.setDoInput(true);
        OutputStream outputStream = httpConnection.getOutputStream();
        // 因客户端浏览器以UTF-8的编码显示数据，固使用OutputStream流向客户端浏览器输出中文，以UTF-8的编码进行输出
        outputStream.write(arg.getBytes(StandardCharsets.UTF_8));
        outputStream.flush();
        // ---取得输入流，并使用Reader读取 ，设置编码utf-8,否则中文乱码
        BufferedReader responseBuffer = new BufferedReader(
                new InputStreamReader((httpConnection.getInputStream()),
                        StandardCharsets.UTF_8));
        String output;
        while ((output = responseBuffer.readLine()) != null) {
            result.append(output);
        }
        return result.toString();
    }

    /**
     * 发送 post请求
     * @param httpUrl 地址
     */
    public byte[] sendHttpPost(String httpUrl) {
        HttpPost httpPost = new HttpPost(httpUrl);
        return sendHttpPost(httpPost);
    }

    /**
     * 发送 post请求
     * @param httpUrl 地址
     * @param maps 参数
     */
    public static byte[] sendHttpPost(String httpUrl, Map<String, Object> maps) {
        HttpPost httpPost = new HttpPost(httpUrl);
        // 创建参数队列
        List<NameValuePair> nameValuePairs = new ArrayList<NameValuePair>();
        for (String key : maps.keySet()) {
            nameValuePairs.add(new BasicNameValuePair(key, maps.get(key).toString()));
        }
        try {
            httpPost.setEntity(new UrlEncodedFormEntity(nameValuePairs, "UTF-8"));
        } catch (Exception e) {
            e.printStackTrace();
        }
        return sendHttpPost(httpPost);
    }

    /**
     * httpClient 发送Post请求
     * @param httpPost
     * @return
     */
    private static byte[] sendHttpPost(HttpPost httpPost) {
        CloseableHttpClient httpClient = null;
        CloseableHttpResponse response = null;
        HttpEntity entity = null;
        byte[] responseContent = null;
        try {
            // 创建默认的httpClient实例.
            httpClient = HttpClients.createDefault();
            RequestConfig requestConfig = RequestConfig.custom()
                    .setSocketTimeout(15000)
                    .setConnectTimeout(15000)
                    .setConnectionRequestTimeout(15000)
                    .build();
            httpPost.setConfig(requestConfig);
            // 执行请求
            response = httpClient.execute(httpPost);
            entity = response.getEntity();
            responseContent = EntityUtils.toByteArray(entity);
            // responseContent = EntityUtils.toString(entity, "UTF-8");  //如果需要返回字符串改这里就行了
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            try {
                // 关闭连接,释放资源
                if (response != null) {
                    response.close();
                }
                if (httpClient != null) {
                    httpClient.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        return responseContent;
    }

    /**
     * 调用接口
     * @param apiPath 请求地址
     * @param type GET & POST
     * @param paramMap map参数
     * @return
     */
    public static String doGetPost(String apiPath, String type, Map<String, Object> paramMap){
        OutputStreamWriter out = null;
        InputStream is = null;
        String result = null;
        try{
            URL url = new URL(apiPath);
            HttpURLConnection connection = (HttpURLConnection) url.openConnection();
            connection.setDoOutput(true);
            connection.setDoInput(true);
            connection.setUseCaches(false);
            connection.setInstanceFollowRedirects(true);
            // 设置请求方式
            connection.setRequestMethod(type);
            // 设置接收数据的格式
            connection.setRequestProperty("Accept", "application/json");
            // 设置发送数据的格式
            connection.setRequestProperty("Content-Type", "application/json");
            connection.setConnectTimeout(10 * 1000);
            connection.setReadTimeout(600 * 1000);
            connection.connect();
            if(type.equals("POST")){
                out = new OutputStreamWriter(connection.getOutputStream(), UTF_8);
                out.append(JSON.toJSONString(paramMap));
                out.flush();
                out.close();
            }
            // 读取响应
            is = connection.getInputStream();
            int length = connection.getContentLength();
            if (length != -1) {
                byte[] data = new byte[length];
                byte[] temp = new byte[512];
                int readLen = 0;
                int destPos = 0;
                while ((readLen = is.read(temp)) > 0) {
                    System.arraycopy(temp, 0, data, destPos, readLen);
                    destPos += readLen;
                }
                result = new String(data, UTF_8);
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                if(is!=null){
                    is.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        return result;
    }

    /**
     * 调用接口
     * @param apiPath 请求地址
     * @param type GET & POST
     * @param jsonStr json格式的数据
     * @return
     */
    public static String sendHttpGetPost(String apiPath, String type, String jsonStr){
        OutputStreamWriter out = null;
        InputStream is = null;
        String result = null;
        try{
            URL url = new URL(apiPath);
            HttpURLConnection connection = (HttpURLConnection) url.openConnection();
            connection.setDoOutput(true);
            connection.setDoInput(true);
            connection.setUseCaches(false);
            connection.setInstanceFollowRedirects(true);
            // 设置请求方式
            connection.setRequestMethod(type);
            // 设置接收数据的格式
            connection.setRequestProperty("Accept", "application/json");
            // 设置发送数据的格式
            connection.setRequestProperty("Content-Type", "application/json");
            connection.setConnectTimeout(10 * 1000);
            connection.setReadTimeout(600 * 1000);
            connection.connect();
            if(type.equals("POST")){
                out = new OutputStreamWriter(connection.getOutputStream(), UTF_8);
                out.append(jsonStr);
                out.flush();
                out.close();
            }
            // 读取响应
            is = connection.getInputStream();
            int length = connection.getContentLength();
            if (length != -1) {
                byte[] data = new byte[length];
                byte[] temp = new byte[512];
                int readLen = 0;
                int destPos = 0;
                while ((readLen = is.read(temp)) > 0) {
                    System.arraycopy(temp, 0, data, destPos, readLen);
                    destPos += readLen;
                }
                result = new String(data, UTF_8);
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                if(is!=null){
                    is.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        return result;
    }


    /**
     * 绕过验证
     *
     * @return
     * @throws NoSuchAlgorithmException
     * @throws KeyManagementException
     */
    public static SSLContext createIgnoreVerifySSL() throws NoSuchAlgorithmException, KeyManagementException {
        SSLContext sc = SSLContext.getInstance("SSLv3");

        // 实现一个X509TrustManager接口，用于绕过验证，不用修改里面的方法
        X509TrustManager trustManager = new X509TrustManager() {
            @Override
            public void checkClientTrusted(java.security.cert.X509Certificate[] paramArrayOfX509Certificate,
                                           String paramString) {
            }

            @Override
            public void checkServerTrusted(java.security.cert.X509Certificate[] paramArrayOfX509Certificate,
                                           String paramString) {
            }

            @Override
            public java.security.cert.X509Certificate[] getAcceptedIssuers() {
                return null;
            }
        };

        sc.init(null, new TrustManager[]{trustManager}, null);
        return sc;
    }

    /**
     * 获取IP地址
     * 使用Nginx等反向代理软件， 则不能通过request.getRemoteAddr()获取IP地址
     * 如果使用了多级反向代理的话，X-Forwarded-For的值并不止一个，而是一串IP地址，X-Forwarded-For中第一个非unknown的有效IP字符串，则为真实IP地址
     */
    public static String getIpAddr() {
        HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
        String ip = null;
        try {
            ip = request.getHeader("x-forwarded-for");
            if (org.springframework.util.StringUtils.isEmpty(ip) || "unknown".equalsIgnoreCase(ip)) {
                ip = request.getHeader("Proxy-Client-IP");
            }
            if (org.springframework.util.StringUtils.isEmpty(ip) || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
                ip = request.getHeader("WL-Proxy-Client-IP");
            }
            if (org.springframework.util.StringUtils.isEmpty(ip) || "unknown".equalsIgnoreCase(ip)) {
                ip = request.getHeader("HTTP_CLIENT_IP");
            }
            if (org.springframework.util.StringUtils.isEmpty(ip) || "unknown".equalsIgnoreCase(ip)) {
                ip = request.getHeader("HTTP_X_FORWARDED_FOR");
            }
            if (org.springframework.util.StringUtils.isEmpty(ip) || "unknown".equalsIgnoreCase(ip)) {
                ip = request.getRemoteAddr();
            }
        } catch (Exception e) {
            log.error("getIpAddr ERROR ", e);
        }
        // 使用代理，则获取第一个IP地址
        if (!org.springframework.util.StringUtils.isEmpty(ip) && ip.length() > 15) {
            if (ip.indexOf(",") > 0) {
                ip = ip.substring(0, ip.indexOf(","));
            }
        }
        return ip;
    }

    /**
     * 获取本机所有IP
     * @return
     */
    public static Map<String,Object> getIpAddress() {
        Map<String,Object> resultMap = new HashMap<>();
        try {
            InetAddress addr1 = InetAddress.getLocalHost();
            resultMap.put(addr1.getHostName(),addr1.getHostAddress());

            Enumeration<NetworkInterface> faces = NetworkInterface.getNetworkInterfaces();
            while (faces.hasMoreElements()) {
                /* 遍历网络接口*/
                NetworkInterface face = faces.nextElement();
                if (face.isLoopback() || face.isVirtual() || !face.isUp()) {
                    continue;
                }
                /**System.out.print("网络接口名：" + face.getDisplayName() + "，地址：");*/
                Enumeration<InetAddress> address = face.getInetAddresses();
                while (address.hasMoreElements()) {
                    /*遍历网络地址*/
                    InetAddress addr = address.nextElement();
                    if (!addr.isLoopbackAddress() && addr.isSiteLocalAddress() && !addr.isAnyLocalAddress()) {
                        resultMap.put(face.getDisplayName(),addr.getHostAddress());
                    }
                }
            }
        } catch (SocketException | UnknownHostException e) {
            e.printStackTrace();
        }
        return resultMap;
    }

    /**
     * 获取本机所有IP
     * @return
     */
    public static List getLocalIPList()
    {
        List localIps = new ArrayList();
        InetAddress ip = null;
        try {
            for(Enumeration netInterfaces = NetworkInterface.getNetworkInterfaces(); netInterfaces.hasMoreElements();)
            {
                NetworkInterface ni = (NetworkInterface)netInterfaces.nextElement();
                for(Enumeration ips = ni.getInetAddresses(); ips.hasMoreElements();)
                {
                    ip = (InetAddress)ips.nextElement();
                    if(ip.isSiteLocalAddress() && !ip.isLoopbackAddress() && ip.getHostAddress().indexOf(":") == -1) {
                        localIps.add(ip.getHostAddress());
                    }
                }
            }
        }
        catch(Exception e) {
            e.printStackTrace();
        }
        return localIps;
    }

    /**
     * 从请求中获取真实的ip地址
     * @param request
     * @return
     */
    public static String getIpAddr(HttpServletRequest request) {
        String ip = request.getHeader("X-Forwarded-For");
        if(StringUtils.isNotEmpty(ip) && !"unKnown".equalsIgnoreCase(ip)){
            //多次反向代理后会有多个ip值，第一个ip才是真实ip
            int index = ip.indexOf(",");
            if(index != -1){
                return ip.substring(0,index);
            }else{
                return ip;
            }
        }
        ip = request.getHeader("X-Real-IP");
        if(StringUtils.isNotEmpty(ip) && !"unKnown".equalsIgnoreCase(ip)){
            return ip;
        }
        return request.getRemoteAddr();
    }

}
